Skip to main content

2. Static decorations

One of the feature advantage of Wolfram Mathematica and WLJS Notebook is a multimodal cells with a powerful syntax sugar. A visual representation of an instance of an object makes the programming experience more educative for sure.

Summary Item

The easiest way of providing a bit more information, but still keeping the actual expression intact is to use ArrangeSummaryBox

StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{},
With[{
summary = {BoxForm`SummaryItem[{"State: ", s["State"]}]}
},
BoxForm`ArrangeSummaryBox[
StateMachine,
s,
None,
summary,
Null
]
]
]

Here we redefined a standard output form to a decorated summary box, providing the visible state field

StateMachine["State" -> 3]

info

Despite the fact of looking different, you can still work with it normally: setting and getting properties, i.e.

is 100% valid

Custom decorations

One can do all decorations from scratch using Graphics for instance

StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{},
With[{
g = Graphics[{ Opacity[0.5],
Table[
Rotate[{
Hue[i/12.0, 1.0, 0.5],
Rectangle[{-1,-1}, {1,1}]
}, i / (s["State"]+1)]
, {i, 0, 6Pi, Pi}]
}, ImageSize->{100,100}, ImagePadding->None]
},
ViewBox[s, g]
]
]

The result will look like

machine = StateMachine[]

StateMachineChange[machine, 2]

Summary Item and Custom decoration

Why not to merge both leaving the graphics as an icon?

StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{},
With[{
summary = {BoxForm`SummaryItem[{"State: ", s["State"]}]},
icon = Graphics[{ Opacity[0.5],
Table[
Rotate[{
Hue[i/12.0, 1.0, 0.5],
Rectangle[{-1,-1}, {1,1}]
}, i / (s["State"]+1), {0.,0.}]
, {i, 0, 6Pi, Pi}]
}, ImageSize->{50,50}, AspectRatio->1, ImagePadding->None]
},
BoxForm`ArrangeSummaryBox[
StateMachine,
s,
icon,
summary,
Null
]
]
]
machine = StateMachine["State"->2]

Javascript decoration

There is also an option to use pure Javascript to render an object, let us make our layout much simpler starting with

StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{},
ViewBox[s, CustomDecorator[s["State"]]]
]

Here we mentioned CustomDecorator which is going to be our WLJS Function

Then, create a new cell

.js

core.CustomDecorator = async (args, env) => {
const state = await interpretate(args[0], env);
const element = env.element;

element.classList.add('flex', 'rounded-md', 'p-2');
element.style.border = "1px solid #999";
element.style.boxShadow = "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)";

element.style.transitionDuration = '0.8s';
element.style.transitionProperty = 'transform';

setTimeout(() => {
element.style.transform = "rotate(360deg)";
}, 100);

element.innerText = state;
}

The result will be following

Animated decoration in Summary Item

Why not also animate it using Wolfram Language?

Let it be many balls bouncing the walls. Firstly let us make proof of concept

test
balls = RandomReal[{-1,1}, {4,2}];
velocities = RandomReal[{-1,1}, {4,2}];

EventHandler["animate", Function[Null,
{balls, velocities} = Map[With[{
v = {If[Abs[#[[1,1]]] >= 1, -1, 1], If[Abs[#[[1, 2]]] >= 1, -1, 1]} #[[2]],
p = #[[1]]
},
{p + 0.2 v, v}
]&, Transpose[{balls, velocities}]] // Transpose;
]]

Graphics[{PointSize[0.03], Point[balls // Offload], AnimationFrameListener[balls // Offload, "Event"->"animate"]}, PlotRange->{{-1,1}, {-1,1}}, TransitionType->None]

Now we need only to scope our variables and embed it to summary item

StateMachine /: MakeBoxes[s: StateMachine[symbol_Symbol?AssociationQ], form: (StandardForm | TraditionalForm)] := Module[{
balls = RandomReal[{-1,1}, {s["State"],2}],
velocities = RandomReal[{-1,1}, {s["State"],2}],
animateEvent = CreateUUID[]
},

EventHandler[animateEvent, Function[Null,
{balls, velocities} = Map[With[{
v = {If[Abs[#[[1,1]]] >= 1, -1, 1], If[Abs[#[[1, 2]]] >= 1, -1, 1]} #[[2]],
p = #[[1]]
},
{p + 0.2 v, v}
]&, Transpose[{balls, velocities}]] // Transpose;
]];

With[{
summary = {BoxForm`SummaryItem[{"State: ", s["State"]}]},
icon = Graphics[{
PointSize[0.03], Point[balls // Offload],
AnimationFrameListener[balls // Offload, "Event"->animateEvent]
},
PlotRange->{{-1,1}, {-1,1}},
TransitionType->None,
ImageSize->{50,50},
AspectRatio->1,
ImagePadding->None
]
},
BoxForm`ArrangeSummaryBox[
StateMachine,
s,
icon,
summary,
Null
]
]
]

Then let us see the result

machine = StateMachine["State"->2]